package io.summer.gadget.channel.core;

import io.summer.basis.tool.IpTool;
import io.summer.gadget.RedisFacade;
import io.summer.redis.tool.RedisTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.web.util.UriComponents;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author ml
 */
@Slf4j
public class ServiceCheck extends AbstractServiceHelper {
  private final static String HEALTH_CHECK_LOCK = "service:check:lock";

  /**
   * 检查间隔30秒
   */
  private static final int CHECK_INTERVAL = 30;

  @Autowired
  private RedisTool redisTool;

  private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

  private final DefaultRedisScript<Boolean> HEALTH_CHECK_LOCK_SCRIPT = new DefaultRedisScript<>(
    "local now = redis.call('time')[1]" +
      "  local k = KEYS[1]" +
      "  local old = redis.call('get', k)" +
      "  if not old or now >= old then" +
      "      return redis.call('set', k, now + ARGV[1])" +
      "  end", Boolean.class);

  private Boolean getLock() {
    return redisTool.stringTemplate().execute(HEALTH_CHECK_LOCK_SCRIPT,
      Collections.singletonList(HEALTH_CHECK_LOCK), ((Integer) ServiceCheck.CHECK_INTERVAL).toString());
  }

  private void doAliveCheck() {
    for (String service : redisTool.keys(getServiceAllKey())) {
      List<String> servers = RedisFacade.hashFields(service);
      for (String server : servers) {
        log.info("check service: {}@{}", service, server);
        UriComponents parts = parseUrl(server);
        int port = parts.getPort();
        if (!IpTool.checkPort(port)
          || !IpTool.isConnectable(parts.getHost(), port)) {
          String name = getAppName(service);
          log.warn("service instance is not available: {}@{}", name, server);
          unregisterServer(name, server);
        }
      }
    }
  }

  public void startAliveCheck() {
    scheduler.scheduleWithFixedDelay(() -> {
      if (getLock()) {
        log.info("do service alive check...");
        doAliveCheck();
      }
    }, 5, CHECK_INTERVAL, TimeUnit.SECONDS);
  }
}
